


import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;


public class ClientServerFrame extends JFrame implements ConnectorListener, AcceptorListener
{
	private JTextField nameField;
	private JTextField addressField;
	private JTextField portField;
	private JTextField listenPortField;
	private JTextField descriptionField;
	private JTextField chatField;
	
	private JButton connectButton;
	private JButton pictureButton;
	private JButton listenButton;
	private JButton sendTextButton;
	
	private DefaultListModel statusListModel;
	private File selectedFile;
	private String fileName;
	
	private JLabel drawPicture;
	
	private JMenuBar menuBar;
	private JMenu menu;
	
	private JMenuItem clientMenuItem;
	private JMenuItem serverMenuItem; 
	private JMenuItem uploadImageMenuItem; 
	private JMenuItem closeMenuItem;   
	private JMenuItem acceptImageMenuItem;
	private JMenuItem rejectImageMenuItem;
	private JMenuItem previewImageMenuItem;
	private JMenuItem previewAcceptedImageMenuItem;
	
	private GridBagLayout layout;
	
	
	private	byte[] fileBytes; 
	private int fileLength;
	
	private Connector connector;
	private Acceptor acceptor;
	
	
	public ClientServerFrame()
	{
		
		setSize(100, 250);
		setTitle("Picture Client & Server Program");
		setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
		
		
		buildUI();
		
		connector = null;
		acceptor = null;
	}


	private void buildUI()
	{
		BoxLayout box = new BoxLayout(getContentPane(), BoxLayout.X_AXIS );
		getContentPane().setLayout(box);
		
		// The follow code is used to construct the menu bar with its
		// various commands.
		menu = new JMenu("Select Options");
		menu.setMnemonic(KeyEvent.VK_A);
	
		menuBar = new JMenuBar();
		menuBar.add(menu);
		
		
		clientMenuItem = new JMenuItem("I am a client.");
		serverMenuItem = new JMenuItem("I am a server.");
		uploadImageMenuItem = new JMenuItem("Upload image.");
		closeMenuItem = new JMenuItem("Close application.");
		acceptImageMenuItem = new JMenuItem("Accept image.");
		rejectImageMenuItem = new JMenuItem("Reject image.");
		previewImageMenuItem = new JMenuItem("Preview uploaded image.");
		previewAcceptedImageMenuItem = new JMenuItem("Preview accepted image.");
		
		
		
		menu.add(clientMenuItem);		
		menu.add(serverMenuItem);
		menu.addSeparator();
		
		menu.add(uploadImageMenuItem);
		menu.add(previewImageMenuItem);
		menu.addSeparator();
		
		menu.add(acceptImageMenuItem);
		menu.add(rejectImageMenuItem);
		menu.add(previewAcceptedImageMenuItem);
		menu.addSeparator();
		
		menu.add(closeMenuItem);
		
		uploadImageMenuItem.setEnabled(false);
		acceptImageMenuItem.setEnabled(false);
		rejectImageMenuItem.setEnabled(false);
		previewImageMenuItem.setEnabled(false);
		previewAcceptedImageMenuItem.setEnabled(false);
		
		
		// Various action listeners for menu items.
		
		clientMenuItem.addActionListener(
				new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				clientMenuItem.setEnabled(false);
				serverMenuItem.setEnabled(true);
				uploadImageMenuItem.setEnabled(true);
				previewAcceptedImageMenuItem.setEnabled(false);
				buildClient();
			}
		}
				);
		
		serverMenuItem.addActionListener(
				new ActionListener()
		{
			public void actionPerformed(ActionEvent e)
			{
				serverMenuItem.setEnabled(false);
				clientMenuItem.setEnabled(true);
				uploadImageMenuItem.setEnabled(false);
				previewImageMenuItem.setEnabled(false);
				buildServer();
			}
		}
				);
		
		
		
		uploadImageMenuItem.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent e)
					{
						sendPicture(uploadImageMenuItem);
					}
				}
						);
		
		acceptImageMenuItem.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent e)
					{
						acceptor.acceptImage();
						listenButton.setEnabled(true);
						acceptImageMenuItem.setEnabled(false);
						rejectImageMenuItem.setEnabled(false);
					}
				}
						);
		
		rejectImageMenuItem.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent e)
					{
						acceptor.rejectImage();
						acceptImageMenuItem.setEnabled(false);
						rejectImageMenuItem.setEnabled(false);
						statusListModel.addElement("- Image has been rejected!");
						
						listenButton.setEnabled(true);
					}
				}
						);
		
		previewImageMenuItem.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent e)
					{
						drawPicture2(fileBytes);
					}
				}
						);
		
		previewAcceptedImageMenuItem.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent e)
					{
						drawPicture2(fileBytes);
					}
				}
						);
		
		
		
		closeMenuItem.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent e)
					{
						dispose();
					}
				}
						);
		
		
		/*
		layout.setConstraints(
				menuBar,
				new GridBagConstraints(
					1, 0, 1, 1, 0.0, 0.0,
					GridBagConstraints.WEST, GridBagConstraints.NONE,
					new Insets(10, 10, 10, 10), 0, 0));
			
		*/
		getContentPane().add(menuBar);
	
			
	// If user chooses to be a client this method is run.
		
	}
	
	public void buildClient()
	{
		layout = new GridBagLayout();
		setSize(600, 700);
		getContentPane().removeAll();
		getContentPane().setLayout(layout);
		
		repaint();
		
		setTitle("--- [  Client Frame  ] ---");
		
		layout.setConstraints(
				menuBar,
				new GridBagConstraints(
					0, 0, 1, 1, 0.0, 0.0,
					GridBagConstraints.WEST, GridBagConstraints.NONE,
					new Insets(10, 10, 10, 10), 0, 0));
			getContentPane().add(menuBar);
			
		JLabel nameLabel = new JLabel("Client Name: ");
		layout.setConstraints(
			nameLabel,
			new GridBagConstraints(
				0, 1, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(nameLabel);
		
		nameField = new JTextField();
		layout.setConstraints(
			nameField,
			new GridBagConstraints(
				1, 1, 1, 1, 1.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(nameField);
		
		JLabel addressLabel = new JLabel("IP Address:");
		layout.setConstraints(
			addressLabel,
			new GridBagConstraints(
				0, 2, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(addressLabel);
		
		addressField = new JTextField();
		layout.setConstraints(
			addressField,
			new GridBagConstraints(
				1, 2, 1, 1, 1.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(addressField);
		
		JLabel portLabel = new JLabel("Port: ");
		layout.setConstraints(
			portLabel,
			new GridBagConstraints(
				0, 3, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(portLabel);
		
		portField = new JTextField();
		layout.setConstraints(
			portField,
			new GridBagConstraints(
				1, 3, 1, 1, 1.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(portField);
		
		JLabel descriptionLabel = new JLabel("Description of Image: ");
		layout.setConstraints(
				descriptionLabel,
				new GridBagConstraints(
					0, 4, 1, 1, 0.0, 0.0,
					GridBagConstraints.WEST, GridBagConstraints.NONE,
					new Insets(10, 10, 10, 10), 0, 0));
			getContentPane().add(descriptionLabel);
			
			descriptionField = new JTextField();
			layout.setConstraints(
				descriptionField,
				new GridBagConstraints(
					1, 4, 1, 1, 1.0, 0.0,
					GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
					new Insets(10, 10, 10, 10), 0, 0));
			getContentPane().add(descriptionField);
			descriptionField.setEnabled(true);
			
		connectButton = new JButton("Connect & Send Picture to Server");
		connectButton.addActionListener(
			new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					connect();
				}
			});
		layout.setConstraints(
			connectButton,
			new GridBagConstraints(
				0, 6, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(connectButton);
		connectButton.setEnabled(true);
		
		
		JLabel statusLabel = new JLabel("Status");
		layout.setConstraints(
			statusLabel,
			new GridBagConstraints(
				0, 7, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(statusLabel);
		
		
		
		statusListModel = new DefaultListModel();
		JList statusList = new JList(statusListModel);
		JScrollPane statusScroller = new JScrollPane(statusList);
		layout.setConstraints(
			statusScroller,
			new GridBagConstraints(
				0, 8, 2, 1, 1.0, 1.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(statusScroller);
		statusListModel.addElement("- Please use the 'Select Options' Menu");
		statusListModel.addElement("  in order to upload an image before connecting.");
		repaint();
		setVisible(true);
		
		JLabel chatLabel = new JLabel("Enter chat message");
		layout.setConstraints(
			chatLabel,
			new GridBagConstraints(
				0, 9, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(2, 2, 2, 2), 0, 0));
		getContentPane().add(chatLabel);
		
		chatField = new JTextField();
		layout.setConstraints(
			chatField,
			new GridBagConstraints(
				1, 9, 1, 1, 1.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(chatField);
		
		sendTextButton = new JButton("Send Text Message");
		sendTextButton.addActionListener(
			new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					sendText();
				}
			});
		layout.setConstraints(
			sendTextButton,
			new GridBagConstraints(
				0, 10, 1, 1, 0.0, 0.0,
				GridBagConstraints.EAST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(sendTextButton);

		
	}
	
	// If the user chooses to be a server, this method is run.
	
	public void buildServer()
	{
		layout = new GridBagLayout();
		setSize(500, 700);
		getContentPane().removeAll();
		getContentPane().setLayout(layout);
		repaint();
		setTitle("--- [  Server Frame  ] ---");
		
		layout.setConstraints(
				menuBar,
				new GridBagConstraints(
					0, 0, 1, 1, 0.0, 0.0,
					GridBagConstraints.CENTER, GridBagConstraints.NONE,
					new Insets(10, 10, 10, 10), 0, 0));
			getContentPane().add(menuBar);
		
		JLabel nameLabel = new JLabel("Server Name: ");
		layout.setConstraints(
			nameLabel,
			new GridBagConstraints(
				0, 1, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(nameLabel);
		
		nameField = new JTextField();
		layout.setConstraints(
			nameField,
			new GridBagConstraints(
				1, 1, 1, 1, 1.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(nameField);
		
		JLabel listenPortLabel = new JLabel("Listen Port:");
		layout.setConstraints(
			listenPortLabel,
			new GridBagConstraints(
				0, 2, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(listenPortLabel);
		
		listenPortField = new JTextField();
		layout.setConstraints(
			listenPortField,
			new GridBagConstraints(
				1, 2, 1, 1, 1.0, 0.0,
				GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(listenPortField);
		
		listenButton = new JButton("Listen");
		listenButton.addActionListener(
			new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					listen();
				}
			});
		layout.setConstraints(
			listenButton,
			new GridBagConstraints(
				1, 3, 1, 1, 0.0, 0.0,
				GridBagConstraints.EAST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(listenButton);
		
		JLabel statusLabel = new JLabel("Status");
		layout.setConstraints(
			statusLabel,
			new GridBagConstraints(
				0, 4, 1, 1, 0.0, 0.0,
				GridBagConstraints.WEST, GridBagConstraints.NONE,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(statusLabel);
		
		statusListModel = new DefaultListModel();
		JList statusList = new JList(statusListModel);
		JScrollPane statusScroller = new JScrollPane(statusList);
		layout.setConstraints(
			statusScroller,
			new GridBagConstraints(
				0, 5, 2, 1, 1.0, 1.0,
				GridBagConstraints.CENTER, GridBagConstraints.BOTH,
				new Insets(10, 10, 10, 10), 0, 0));
		getContentPane().add(statusScroller);
		setVisible(true);
		
	}


	private void connect()
	{
		String name = nameField.getText().trim();
		
		if (name.length() == 0)
		{
			JOptionPane.showMessageDialog(
				this, "Please specify a name", "Validation Error",
				JOptionPane.ERROR_MESSAGE);
			
			return;
		}
		
		String address = addressField.getText().trim();
		
		if (address.length() == 0)
		{
			JOptionPane.showMessageDialog(
				this, "Please specify an address", "Validation Error",
				JOptionPane.ERROR_MESSAGE);
			
			return;
		}
		
		int port;
		
		try
		{
			port = Integer.parseInt(portField.getText());
		}
		catch (NumberFormatException e)
		{
			JOptionPane.showMessageDialog(
				this, "Please specify a number for the port between 0 and 65535",
				"Validation Error", JOptionPane.ERROR_MESSAGE);
			
			return;
		}
		
		if (port < 0 || port > 65535)
		{
			JOptionPane.showMessageDialog(
				this, "Please specify a number for the port between 0 and 65535",
				"Validation Error", JOptionPane.ERROR_MESSAGE);
			
			return;
		}
		
		String description = descriptionField.getText();
		
		if (description.length() == 0)
		{
			JOptionPane.showMessageDialog(
				this, "Please write a description to be sent with the image.", "Validation Error",
				JOptionPane.ERROR_MESSAGE);
			
			return;
		}
		
		connectButton.setEnabled(false);
		statusListModel.addElement("- Connecting to " + address + " on port " + port);
		
		connector = new Connector(name, address, port, fileBytes, fileLength, description);
		connector.addConnectorListener(this);
		connector.start();
	
	}
	
	
	
	
	public void listen()
	{
		String name = nameField.getText().trim();
		
		if (name.length() == 0)
		{
			JOptionPane.showMessageDialog(
				this, "Please specify a name.", "Validation Error",
				JOptionPane.ERROR_MESSAGE);

			return;
		}

		int listenPort;
		
		try
		{
			listenPort = Integer.parseInt(listenPortField.getText());
		}
		catch (NumberFormatException e)
		{
			JOptionPane.showMessageDialog(
				this, "Please specify the port as a number between 0 and 65535",
				"Validation Error", JOptionPane.ERROR_MESSAGE);
			
			return;
		}
		
		if (listenPort < 0 || listenPort > 65535)
		{
			JOptionPane.showMessageDialog(
				this, "Please specify the port as a number between 0 and 65535",
				"Validation Error", JOptionPane.ERROR_MESSAGE);
			
			return;
		}

		
		listenButton.setEnabled(false);


		statusListModel.addElement("- Waiting for connection.");
		acceptor = new Acceptor(name, listenPort);
		acceptor.addAcceptorListener(this);
		acceptor.start();
	}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	public void sendPicture(JButton buttonPressed)
	{
		JFileChooser chooser = new JFileChooser();
		FileNameExtensionFilter filter = new FileNameExtensionFilter(
		        "JPG & GIF & PNG Images", "jpg", "gif", "png");
		    chooser.setFileFilter(filter);
		    chooser.showOpenDialog(buttonPressed);
		    
				selectedFile = chooser.getSelectedFile();
		        fileName = chooser.getSelectedFile().getPath();
		        
		        try 
		        {
					drawPicture();
				} 
		        catch (IOException e) 
		        {				
					e.printStackTrace();
				}
	}
	
	public void sendPicture(JMenuItem buttonPressed)
	{
		JFileChooser chooser = new JFileChooser();
		FileNameExtensionFilter filter = new FileNameExtensionFilter(
		        "JPG & GIF & PNG Images", "jpg", "gif", "png");
		    chooser.setFileFilter(filter);
		    chooser.showOpenDialog(buttonPressed);
		    
				selectedFile = chooser.getSelectedFile();
		        fileName = chooser.getSelectedFile().getPath();
		        
		        try 
		        {
					drawPicture();
				} 
		        catch (IOException e) 
		        {				
					e.printStackTrace();
				}
		        
		         
	}
	
	public void drawPicture() throws IOException
	{
		fileLength = (int) selectedFile.length();
		System.out.println(selectedFile.getName());
		fileBytes = new byte[fileLength];
		
		
		FileInputStream input = new FileInputStream(selectedFile);
		
		int offset = 0;
		
		while (offset < fileLength)
		{
			int chunkSize = 65536;
			
			if (fileLength - offset < chunkSize)
			{
				chunkSize = fileLength - offset;
			}
			
			offset += input.read(fileBytes, offset, chunkSize);
		}
		
		
		
		input.close();
		
			
		connectButton.setEnabled(true);
		previewImageMenuItem.setEnabled(true);
		descriptionField.setEnabled(true);
		statusListModel.addElement("- Picture entitled: " + "'" + selectedFile.getName() + "'" + " (" + fileLength + " bytes" + ") " + "has been succesfully uploaded.");
		
	}
	
	public void drawPicture2(byte[] fileBytes)
	{
		ImageIcon picture = new ImageIcon(fileBytes);
		int picHeight = picture.getIconHeight();
		int picWidth = picture.getIconWidth();
		
		
		PictureFrame pictureFrame = new PictureFrame(fileBytes, picWidth, picHeight);
		pictureFrame.setVisible(true);
	}

	
	public void connectorSucceeded(
		final String serverAddress, final String serverName)
	{
		EventQueue.invokeLater(
			new Runnable()
			{
				public void run()
				{
					
					statusListModel.addElement(
						"- You are now connected to " + "Server Address: " + serverAddress);
				}
			});
	}

	
	public void connectorFailed(
		final String serverAddress, final Exception reason)
	{
		EventQueue.invokeLater(
			new Runnable()
			{
				public void run()
				{
					connectButton.setEnabled(true);
					
					statusListModel.addElement("- Unable to establish a connection!");
					
					if (serverAddress != null)
					{
						statusListModel.addElement(
							"While connected to " + serverAddress);
					}

					statusListModel.addElement("- " + reason.getClass().getName());
					statusListModel.addElement("- " + reason.getMessage());
				}
			});
	}
	

	public void disposeClient()
	{
		if (connector != null)
		{
			connector.removeConnectorListener(this);
		
		}
		
		super.dispose();
	}
	
	public void disposeServer()
	{
		if (acceptor != null)
		{
			acceptor.removeAcceptorListener(this);
			//acceptor.close();
		}
		
		super.dispose();
	}


	public void acceptorSucceeded(
			final String clientAddress, final String clientName)
		{
			EventQueue.invokeLater(
				new Runnable()
				{
					public void run()
					{
												
						statusListModel.addElement(
							"- Connection accepted. You are now connected to Client Address: "
							+ clientAddress);
					}
				});
		}
	
	public void acceptorFailed(final String address, final Exception reason)
	{
		EventQueue.invokeLater(
			new Runnable()
			{
				public void run()
				{
					listenButton.setEnabled(true);
					
					statusListModel.addElement("- Connection unaccepted!");
					
					if (address != null)
					{
						statusListModel.addElement(
							"While connected to " + address);
					}

				
					statusListModel.addElement("- " + reason.getClass().getName());
					statusListModel.addElement("- " + reason.getMessage());
				}
			});
	}

	
	public void pictureAccepted(final byte[] bytes, final String description)
	{
	
		EventQueue.invokeLater(
			new Runnable()
			{
				public void run()
				{
					fileBytes = bytes;
					previewAcceptedImageMenuItem.setEnabled(true);
					statusListModel.addElement("- You have received the image succesfully!");
					statusListModel.addElement("- Description of image: " + description);
				}
			});
	}
	
	public void pictureSent()
	{
		EventQueue.invokeLater(
				new Runnable()
				{
					public void run()
					{
						connectButton.setEnabled(true);
						statusListModel.addElement("- " + selectedFile.getName() + " has been sent!");
					}
				});
		
	}
	
	public void pictureQuery()
	{
		EventQueue.invokeLater(
				new Runnable()
				{
					public void run()
					{
						acceptImageMenuItem.setEnabled(true);
						rejectImageMenuItem.setEnabled(true);
						statusListModel.addElement("- Would you like to accept or reject the image proposed?");
					}
				});
		
	}
	
	public void rejected()
	{
		EventQueue.invokeLater(
				new Runnable()
				{
					public void run()
					{
						acceptImageMenuItem.setEnabled(true);
						rejectImageMenuItem.setEnabled(true);
						connectButton.setEnabled(true);
						statusListModel.addElement("- Server has rejected your request!");
					}
				});	
	}
	
	public void completedServer()
	{
		EventQueue.invokeLater(
				new Runnable()
				{
					public void run()
					{
						statusListModel.addElement("- Connection complete. Connection closed.");
					}
				});	
	}
	
	public void completedClient()
	{
		EventQueue.invokeLater(
				new Runnable()
				{
					public void run()
					{
						statusListModel.addElement("- Connection complete. Connection closed.");
					}
				});	
	}
	
	public void sendText()
	{
		String text = chatField.getText().toString();
		connector.sendText(text);
		
	}
	
	public void getText(final String line)
	{
		EventQueue.invokeLater(
				new Runnable()
				{
					public void run()
					{
						statusListModel.addElement("Received: " + line);
					}
				});	
	}
	
	
	}
	
	

